package com.acme.bankapp.service.bank;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import com.acme.bankapp.domain.account.Account;
import com.acme.bankapp.domain.account.CheckingAccount;
import com.acme.bankapp.domain.account.SavingAccount;
import com.acme.bankapp.domain.client.Client;
import com.acme.bankapp.domain.client.Gender;
import com.acme.bankapp.exceptions.BankException;

public class BankDataLoaderService {
    private static final String ERROR_GENDER = "Unknown gender!";
    private static final String ERROR_ACCOUNT_TYPE = "Unknown account type!";

    private static final String FIELD_ACCOUNT_TYPE = "accounttype";
    private static final String FIELD_BALANCE = "balance";
    private static final String FIELD_OVERDRAFT = "overdraft";
    private static final String FIELD_NAME = "name";
    private static final String FIELD_GENDER = "gender";

    private static final String GENDER_MALE = "m";
    private static final String GENDER_FEMALE = "f";
    private static final String ACCOUNT_CHECKING = "c";
    private static final String ACCOUNT_SAVING = "s";

    /**
     * Method load clients from the feed file.
     *
     * @param pathFrom
     * @return {@code List<Client>}
     * @throws FileNotFoundException
     */
    public List<Client> loadClients(String pathFrom) throws FileNotFoundException {
        List<Client> clients = new ArrayList<Client>();

        File feed = new File(pathFrom);
        FileInputStream fis = null;
        BufferedReader br = null;

        try {
            fis = new FileInputStream(feed);
            br = new BufferedReader(new InputStreamReader(fis));

            String line;
            try {
                while((line = br.readLine()) != null) {
                    clients.add(parseClientFromFeedString(line));
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (BankException e) {
                e.printStackTrace();
            }
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
                if (br != null) {
                    br.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return clients;
    }

    /**
     * In this method comes a line like:<br/>
     *      "accounttype=c|s;balance=100;overdraft=50;name=John;gender=m|f;"
     *
     * @param feedLine
     * @return Client
     * @throws BankException
     */
    private Client parseClientFromFeedString(String feedLine) throws BankException {
        StringTokenizer st = new StringTokenizer(feedLine, ";");
        Map<String, String> clientFields = new HashMap<String, String>();

        while (st.hasMoreTokens()) {
            fillMapWithClientParams(clientFields, st.nextToken());
        }

        Client client = createClientFromMap(clientFields);
        client.addAccount(createAccountFromMap(clientFields));
        return client;
    }

    private void fillMapWithClientParams(Map<String, String> fields, String token) {
        String [] temp = token.split("=");
        // avoid unknown tokens
        if(temp.length == 2) {
            fields.put(temp[0], temp[1]);
        }
    }

    private Client createClientFromMap(Map<String, String> fields) throws BankException {
        Client client = null;

        // get name
        String name = fields.get(FIELD_NAME);

        // get gender
        Gender gender = null;
        String genderStr = fields.get(FIELD_GENDER);
        if (GENDER_MALE.equals(genderStr)) {
            gender = Gender.MALE;
        } else if (GENDER_FEMALE.equals(genderStr)) {
            gender = Gender.FEMALE;
        } else {
            throw new BankException(ERROR_GENDER);
        }

        client = new Client(name, gender);
        return client;
    }

    private Account createAccountFromMap(Map<String, String> fields) throws BankException {
        Account account = null;
        String accountType = fields.get(FIELD_ACCOUNT_TYPE);
        String balance = fields.get(FIELD_BALANCE);
        String overdraft = fields.get(FIELD_OVERDRAFT);

        if (ACCOUNT_SAVING.equals(accountType)) {
            account = new SavingAccount(0, Double.parseDouble(balance));
        } else if (ACCOUNT_CHECKING.equals(accountType)) {
            account = new CheckingAccount(0, Double.parseDouble(balance), Double.parseDouble(overdraft));
        } else {
            throw new BankException(ERROR_ACCOUNT_TYPE);
        }

        return account;
    }

}
